package com.springframework.core.context;

import com.springframework.core.constant.ScopeType;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Stream;

/**
 * 纯手写ClassPathXmlApplicationContext
 * @author youzhengjie
 */
//说明MyClassPathXmlApplicationContext是ApplicationContext的实现类，可以通过多态的方式创建对象
public class MyClassPathXmlApplicationContext implements ApplicationContext {

    //bean定义Map
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap;

    //单例池,也就是Spring的一级缓存
    private ConcurrentHashMap<String,Object> singletonObjects;

    //beanPostProcessors集合
    private CopyOnWriteArrayList<BeanPostProcessor> beanPostProcessors;

    //root结点下一个结点的集合
    private List<Element> elements;

    //私有化构造器
    private MyClassPathXmlApplicationContext(){
    }

    public MyClassPathXmlApplicationContext(String configLocation){
        this.beanDefinitionMap=new ConcurrentHashMap<>();
        this.singletonObjects=new ConcurrentHashMap<>();
        this.beanPostProcessors=new CopyOnWriteArrayList<>();


        try {
            //获取扫描xml文件,只扫描resources目录下的spring目录
            String uri=this.getClass().getResource("/spring/"+configLocation).toURI().getPath();
            String scp = uri.substring(1, uri.length());
            //通过SAX阅读器去加载XML配置文件，并获取该XML配置文件的root结点也就是my-beans标签
            //然后校验标签是否正确，如果标签不正确则抛出异常
            //通过当前对象的类去拿到类加载器（ClassLoader）
            //获取root结点的下一级结点的集合并且赋值给全局变量elements
            //创建SAX阅读器
            SAXReader saxReader=new SAXReader();
            //加载XML文件
            Document document = saxReader.read(new File(scp));
            //获取root结点
            Element rootElement = document.getRootElement();
            //校验标签的正确性
            if(!rootElement.getName().equals("my-beans")){
                throw new NullPointerException("标签不正确");
            }
            //获取类加载器
            ClassLoader classLoader = this.getClass().getClassLoader();
            //获取root结点的下一级结点的集合
            elements = rootElement.elements();

            //遍历root结点的下一级结点,这个结点就是我们定义的一个个bean
            for (Element element : elements) {
                //校验标签的正确性
                if(!element.getName().equals("my-bean")){
                    throw new NullPointerException("标签不正确");
                }
                //beanName
                String beanName=null;
                //定义一个BeanDefinition
                BeanDefinition beanDefinition = new BeanDefinition();
                //获取root结点的下一级结点的属性
                List<Attribute> attributes = element.attributes();
                //root结点的下一级结点的属性的值
                for (Attribute attribute : attributes) {
                    //分别找出id、class、scope属性
                    if(attribute.getName().equals("id")){
                        beanName=attribute.getValue();
                    }else if(attribute.getName().equals("class")){
                        Class<?> aClass = classLoader.loadClass(attribute.getValue());

                        //判断这个类是否实现BeanPostProcessor
                        if (BeanPostProcessor.class.isAssignableFrom(aClass)) {

                            BeanPostProcessor obj = (BeanPostProcessor) aClass.getConstructor().newInstance();
                            beanPostProcessors.add(obj);
                        }

                        beanDefinition.setType(aClass);
                    }else if(attribute.getName().equals("scope")){
                        beanDefinition.setScope(attribute.getValue());
                    }else {
                        throw new NullPointerException("属性不正确");
                    }
                }
                beanDefinitionMap.put(beanName,beanDefinition);

            }
            //扫描结束



            ConcurrentHashMap.KeySetView<String, BeanDefinition> keySet = beanDefinitionMap.keySet();
            //加载入单例池
            for (String beanName : keySet) {

                BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

                String scope = beanDefinition.getScope();

                if(scope!=null&&scope.equals(ScopeType.PROTOTYPE)){
                    //不做任何事
                }else {
                    if(!singletonObjects.containsKey(beanName)){
                        Object obj = createBean(beanName);
                        singletonObjects.put(beanName,obj);
                    }
                }
            }

        } catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

    }


    @Override
    public Object getBean(String beanName) {

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

        if(beanDefinition==null){
            return null;
        }else {
            String scope = beanDefinition.getScope();

            //如果是多例
            if(scope!=null&&scope.equals(ScopeType.PROTOTYPE)){

                return createBean(beanName);
            }else {//如果是单例的话先去单例池中找

                Object obj = singletonObjects.get(beanName);

                //如果单例池没有
                if(obj==null){

                    obj=createBean(beanName);

                }else {//单例池有的话直接返回obj
                    return obj;
                }
            }
        }
        return null;
    }

    private Object createBean(String beanName) {

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(beanDefinition==null){
            throw new NullPointerException("创建bean失败");
//            return null;
        }else {
            try {

                //依赖注入,二级标签，这既是一个个bean
                for (Element element : elements) {

                    //遍历获取当前beanName
                    String curBeanName = element.attributeValue("id");
                    if(curBeanName.equals(beanName)){
                        Class<?> aClass = beanDefinition.getType();
                        //反射创建对象
                        Object obj = aClass.getConstructor().newInstance();

                        //注入的属性集，也就是第三级标签
                        List<Element> els = element.elements();

                        for (Element el : els) {

                            //只有这种情况才合法，因为我们目前只支持my-properties标签注入
                            if(el.getName().equals("my-properties")){

                                //判断这个标签的子标签是否是my-ref标签
                                if(el.element("my-ref")!=null){
                                    String fieldName = el.attributeValue("name");
                                    String bn = el.element("my-ref").attributeValue("bean");
                                    Field field = aClass.getDeclaredField(fieldName);
                                    field.setAccessible(true);


                                    //通过bn去单例池里面找是否有
                                    if(singletonObjects.containsKey(bn)){
                                        //单例池如果有则用单例池的
                                        Object beanObj = singletonObjects.get(bn);
                                        field.set(obj,beanObj);
                                    }
                                    else{
                                        //直接调用createBean方法创建对象并赋值
                                        Object createBean = createBean(bn);
                                        field.set(obj,createBean);
                                    }
                                }else{
                                    List<Attribute> attributes = el.attributes();

                                    //用于保存注入的字段
                                    String field=null;
                                    //用于保存注入的值
                                    String val=null;

                                    for (Attribute attribute : attributes) {

                                        if(attribute.getName().equals("name")){
                                            field=attribute.getValue();
                                        }else if(attribute.getName().equals("value")){
                                            val=attribute.getValue();
                                        }else {
                                            throw new NullPointerException("请检查定义bean的属性");
                                        }
                                    }
                                    //此时进行依赖注入,必须要是getDeclaredField方法
                                    Field fd = aClass.getDeclaredField(field);
                                    fd.setAccessible(true);

                                    //为了防止注入报错，我们对注入的所有基本数据类型+String进行判断(变小写再判断)
                                    String type = fd.getType().getName().toLowerCase();
                                    if(type.equals("byte")||type.equals("java.lang.byte")){
                                        fd.set(obj,Byte.parseByte(val));
                                    }else if(type.equals("short")||type.equals("java.lang.short")){
                                        fd.set(obj,Short.parseShort(val));
                                    }else if(type.equals("int")||type.equals("java.lang.integer")){
                                        fd.set(obj,Integer.parseInt(val));
                                    }else if(type.equals("long")||type.equals("java.lang.long")){
                                        fd.set(obj,Long.parseLong(val));
                                    }else if(type.equals("double")||type.equals("java.lang.double")){
                                        fd.set(obj,Double.parseDouble(val));
                                    }else if(type.equals("float")||type.equals("java.lang.float")){
                                        fd.set(obj,Float.parseFloat(val));
                                    }else if(type.equals("char")){
                                        fd.set(obj,val.charAt(0));
                                    }else if(type.equals("boolean")||type.equals("java.lang.boolean")){
                                        if(val.equals("true")){
                                            fd.set(obj,true);
                                        }else{
                                            fd.set(obj,false);
                                        }
                                    }else if(type.equals("java.lang.string")){
                                        fd.set(obj,val);
                                    }
                                    else{
                                        throw new NullPointerException("暂不支持这个字段类型");
                                    }
                                }


                            }else {
                                throw new NullPointerException("请检查注入属性的标签");
                            }

                        }
                        //依赖注入完成之后

                        //判断对象是否需要Aware回调
                        if(obj instanceof Aware){

                            //再具体判断是什么回调
                            if(obj instanceof BeanNameAware){
                                ((BeanNameAware) obj).setBeanName(beanName);
                            }
                        }

                        //初始化前
                        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                            beanPostProcessor.postProcessorBeforeInitialization(obj,beanName);
                        }

                        //初始化
                        if(obj instanceof InitializingBean){
                            ((InitializingBean) obj).afterPropertiesSet();
                        }

                        //初始化后
                        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                            beanPostProcessor.postProcessorAfterInitialization(obj,beanName);
                        }

                        //如果是单例就放入单例池
                        if(beanDefinition.getScope()==null||beanDefinition.getScope().equals(ScopeType.SINGLETON)){

                            singletonObjects.put(beanName,obj);
                        }
                        return obj;


                    }
                    }
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }

            //如果找不到就会返回null
            return null;
        }
    }
}
